/**
 * Provides support for contacting the posting owner.  Basically, this class can be seen as an "extension" module
 * to the actual application.  It provides enhanced capabilities including skype and google integration, so
 * that the posting owner can be emailed and/or phoned directly from the browser.
 * @author birdeau@yahoo.com
 */
jsx3.lang.Class.defineClass("gicx.Contact", null, [], function(Contact,Contact_prototype) {


  //since google maps requires a key registered to an http scheme, must use an iframe while in development
  Contact.URL_FOR_POSTING_ADDRESS = "http://remixedby.us/craigslist/google.html";//"http://10.37.129.2:8080/google.html";
  Contact.URL_FOR_DRIVING_DIRECTIONS = "http://maps.google.com/maps?f=d&hl=en&saddr=#&daddr=*&layer=&ie=UTF8";
  Contact.GMAP_RESET_DELAY = 1000;
  var myDrive1Reg = /<table class=\\042ddrsteps/i;
  var myDrive2Reg = /<\/table><\/td><\/tr><\/table>/;
  var myKey = "ABQIAAAAJxqSdTZ58Lguq7exYG0v-RSVkfJqTshsJiN5qt11SlEtOY4OBRQC4IRFiIh7x0mfEnAfLWSAdVQsxQ" //assigned to http://remixedby.us


  /**
   * main method
   */
  Contact_prototype.init = function() {
    //override the url composite for the class; just use this one if passed
    this.jsxsuper();
  };


  Contact.onMapInit = function() {
    var objBlock = gicx.APP.getJSXByName("blkGoogleMap");
    objBlock.setText('<div id="map" style="width: 100%; height: " + objBlock.getAbsolutePosition().H + "px"></div>',true);
    Contact.showPostingAddress();
  };


  /**
   * Navigates the given google map to the given address
   * @returns {String}
   */
  Contact.showPostingAddress = function(strAddress,strPostingId) {
    //only show the address if the map container is expanded
    if(gicx.APP.getJSXByName("tpTools").getSelectedIndex() == 0) {
      if(!(typeof(strAddress) == "string"))
        strAddress = gicx.APP.getJSXByName("txtAddress").getValue() || null;
      var objBlock = gicx.APP.getJSXByName("blkGoogleMap");
      if(window.location.href.toLowerCase().indexOf("file") > -1) {
        //must use iframe during development, given google key issue
        var strSrc = Contact.URL_FOR_POSTING_ADDRESS + ((strAddress)?"?address=" + window.encodeURIComponent(strAddress) + "&":"?");
        strSrc += "jsxheight=" + (objBlock.getAbsolutePosition().H || 200);
        jsx3.log("Navigating google map to geo-address: " + strSrc);
        objBlock.getRendered().childNodes[0].src = strSrc;
        var strHTML = '<iframe src="' + strSrc + '" frameborder="0" style="width:100%;height:' + objBlock.getAbsolutePosition().H + 'px;"></iframe>';
        objBlock.setText(strHTML,true);
      } else {
        //embed when app is live
        jsx3.log('Navigating embedded google map to geo-address: ' + strAddress);
        Contact.load(strAddress,strPostingId);
      }
    }
  };



  /**
   * Creates a new gma and geocoder (if necessary--happens only once); shows an address
   * @param strAddress {String}
   * @param strPostingId {String}
   */
  var map = null;
  var geocoder = null;
  Contact.load = function(strAddress,strPostingId) {
    //update the map height
    var objBlock = gicx.APP.getJSXByName("blkGoogleMap");
    var objGUI = objBlock.getRendered();
    objGUI.childNodes[0].style.height = (objBlock.getAbsolutePosition().H || 200) + "px";

    var bNew;
    if(!map) {
      map = new GMap2(document.getElementById("map"));
      map.addControl(new GSmallZoomControl());
      geocoder = new GClientGeocoder();
      bNew = true;
    }

    //show the given address
    if(strAddress)
      Contact.showAddress(strAddress,strPostingId,bNew);
  };


  /**
   * Places or moves a marker on the gmap
   * @param address {String}
   * @param strPostingId {String}
   * @param bNew {Boolean} if true, this is the first time the map is being used
   */
  Contact.showAddress = function(address,strPostingId,bNew) {
    if (geocoder) {
      geocoder.getLatLng(
        address,
        function(point) {
          if (!point) {
            jsx3.log(address + " not found");
          } else {
            map.checkResize();
            if(bNew)
              map.setCenter(point, bNew?13:null);
            else
              map.panTo(point);

            if(strPostingId) {
              Contact.removeMarker({uri:strPostingId});
              //look for all possible locations for the record; if found get the title as tooltip
              var objCachedRecord = gicx.Cache.getRecord("searchHistory",strPostingId);
              if(!objCachedRecord)
                objCachedRecord = gicx.Cache.getRecord("_searchHistory",strPostingId);
              if(!objCachedRecord)
                objCachedRecord = gicx.getResultsTable().getRecordNode(strPostingId);

              var marker = new GMarker(point,objCachedRecord ? {title:objCachedRecord.getAttribute("jsxtext")} : {title:address});
              map.addOverlay(marker);
              _gMarkerHash[strPostingId] = marker;
              GEvent.addListener(marker, "click", function(marker, point) { gicx.openPosting(strPostingId); });
            }
          }
        }
      );
    }
  };


  /**
   * Removes a given google map marker; TODO: does google do appropriate cleanup GC?
   * @param objEvent {Object} hash with named field, uri
   */
  var _gMarkerHash = {};
  Contact.removeMarker = function(objEvent) {
    var strPostingId = objEvent.uri;
    if(_gMarkerHash[strPostingId]) {
      map.removeOverlay(_gMarkerHash[strPostingId]);
      delete _gMarkerHash[strPostingId];
    }
  };


  /**
   * Gets driving directions from start address to destination address; updates objJSX with an IFrame containing said directions
   * returns null if a valid list of instructions is not returned
   * @param sAddr {String} valid start address (single line, no carriage return, city/state, optional zip)
   * @param dAddr {String} valid end address (single line, no carriage return, city/state, optional zip)
   * @param objJSX {jsx3.gui.Block} object into which an IFRAME will be embedded, that points to the google maps 'driving directions' page
   * @returns {String} HTML or null
   */
  Contact.getDrivingDirections = function(sAddr,dAddr,objJSX) {
    var strURL = Contact.URL_FOR_DRIVING_DIRECTIONS.replace("#",window.encodeURIComponent(sAddr)).replace("*",window.encodeURIComponent(dAddr));
    objJSX.setText('<iframe src="' + strURL + '" border="none" style="width:100%;height:100%;border:solid 1px #000000;"></iframe>',true);
  };


  /**
   * Called when a posting is deactivated; does general cleanup (resetting of UI)
   * @param objEvent {Object}
   */
  Contact.onDeactivatePosting = function(objEvent) {
    //logic here to reset the rolodex controls to their initial empty state
    gicx.APP.getJSXByName("txtAddress").setValue();
    Contact.resetDexControls("email");
    Contact.resetDexControls("phone");
  };


  /**
   * Called when a posting is activated; populates the email, phone, and geo-address fiels
   * @param objEvent {Object} hash with named field, target (the tab that was activated)
   */
  Contact.onActivatePosting = function(objEvent) {
    //update the map location to point to the address specific to the active posting (throttle to avoid unnecessary deltas if the user is quickly chaning tabs)
    var objTab = objEvent.target;
    if(objTab && objTab == gicx.Posting.getActive()) {
      //resolve the record in the data model (postings) that is associated with the tab that is now active (try the hard, soft, and results caches for the record)
      var objRecord = gicx.Cache.getRecord("searchHistory",objTab.postingurl);
      if(!objRecord) objRecord = gicx.Cache.getRecord("_searchHistory",objTab.postingurl);
      if(!objRecord) objRecord = gicx.getResultsTable().getRecordNode(objTab.postingurl);
      if(objRecord) {
        //create and populate the controls necessary to show the user's saved emails and phone numbers for the given posting
        var strAddress = objRecord.getAttribute("address");
        if(jsx3.util.strEmpty(strAddress))
          strAddress = objRecord.getAttribute("loc");
        gicx.APP.getJSXByName("txtAddress").setValue(strAddress?strAddress:"");
        Contact.showPostingAddress(strAddress?strAddress:null,objTab.getName());
        Contact.createDexControls(objRecord,"email","email","jsxid","CONTACT_EMAIL_DID_CHANGE");
        Contact.createDexControls(objRecord,"phone","phone","jsxid","CONTACT_NUMBER_DID_CHANGE");
      } else {
        jsx3.log("error getting from data cache: " + objTab.postingurl);
      }
    }
  };


  /**
   * Removes ephemeral objects from the contact control (phone and email links)
   * @param strGroup {String} name of parent control from which to remove children
   */
  Contact.resetDexControls = function(strGroup) {
    //remove any of the non-default fields; set field1 value to null
    var objTP = gicx.APP.getJSXByName(strGroup);
    for(var i=objTP.getChildren().length-2;i>=0;i--)
      objTP.removeChild(i);
  };


  /**
   * Creates a set of link objects (email or phone) and adds to the UI; The link objects can be clicked to make a phone call or send an email
   * @param objRecord {jsx3.xml.Entity} active record
   * @param strGroup {String} name of parent control from which to remove children
   * @param strEntityName {String} name of child node contained by CDF record that holds the phone or email info
   * @param strAttName {String}
   * @param strSubject {String} subject to publish to once a control is added
   */
  Contact.createDexControls = function(objRecord,strGroup,strEntityName,strAttName,strSubject) {
    //always make sure to reset before adding in case an error stopped normal cleanup (shouldn't happen, but need to be sure)
    Contact.resetDexControls(strGroup);

    //add and update all fields
    var objTP = gicx.APP.getJSXByName(strGroup);
    var i = 0;
    var iterator = objRecord.selectNodeIterator(strEntityName);
    //throttle at three for performance reasons
    var oCheck= {};
    while(iterator.hasNext()) {
      var objNode = iterator.next();
      var strValue = objNode.getAttribute(strAttName);
      if(oCheck[strValue] || i == 3)
        continue;
      else
        oCheck[strValue]=1;
      var objTab = Contact.addDexItem(objTP);
      if(objTab) {
        objTab.getChild('txtContactObject').setValue(strValue);
        //when this is a system event, not need to echo back the change
        gicx.publish({subject:strSubject,value:strValue,jsxid:objTab.getChild('txtContactObject').getId(),system:true});
      }
      i++;
    }
    objTP.adoptChild(objTP.getChild("addContactItem"));
  };


  /**
   * Subscribed to the onchange event for the contact phone number;
   * Updates the given field (an HTML anchor tag) with the number appropriate for skype
   * @param objEvent {Object}
   */
  Contact.onUpdatePhone = function(objEvent) {
    //update the hyperlink in the UI
    var intNumber = objEvent.value.replace(/[^0-9a-zA-Z]/g,"");
    var strSkypeLink = "skype:" + (!isNaN(intNumber)?"+1":"") + intNumber + "?call";
    gicx.APP.getJSXById(objEvent.jsxid).getParent().getChild('lblContactObject').setText('<a title="Click to telephone the posting owner via Skype" style="color:black" href="' + strSkypeLink + '" onclick="gicx.Audit.phone(\'' + (intNumber+"").replace("'",'"') + '\');return skypeCheck();">' + objEvent.value + '</a>',true);

    //save the modified dex data to the hard cache (so user values are persisted beyond this session)
    if(!objEvent.system)
      Contact.cacheDexItem(gicx.APP.getJSXById(objEvent.jsxid));
  };


  /**
   * Subscribed to the onchange event for the contact email;
   * Updates the given field (an HTML anchor tag) with the mailto format appropriate for sending an email
   * @param objEvent {Object}
   */
  Contact.onUpdateEmail = function(objEvent) {
    //update the hyperlink in the UI
    var objRecordNode = gicx.Posting.getActiveRecord();
    gicx.APP.getJSXById(objEvent.jsxid).getParent().getChild('lblContactObject').setText('<a title="Click to send an email to the posting owner" style="color:black" href="mailto:' + objEvent.value + ((objRecordNode)?'?subject='+window.encodeURIComponent(objRecordNode.getAttribute('jsxtext')):'') + '" onclick="gicx.Audit.email(\'' + objEvent.value.replace("'",'"') + '\',\'' +  ((objRecordNode)?window.encodeURIComponent(objRecordNode.getAttribute('jsxtext').replace("'",'"')):'') + '\');">' + objEvent.value + '</a>',true);

    //save the modified dex data to the hard cache (so user values are persisted beyond this session)
    if(!objEvent.system)
      Contact.cacheDexItem(gicx.APP.getJSXById(objEvent.jsxid));
  };


  /**
   * Called by onchange event for the text entry field for a given contact item; updates the actie posting with the entire updated rolodex
   * @param objTP {jsx3.gui.Block} Parent containing the Dex controls to save
   */
  Contact.cacheDexItem = function(objTP) {
    var objRecord = gicx.Posting.getActiveRecord();
    if(objRecord) {

      //remove existing items
      var strEntityName = objTP.getName();
      var objNodes = objRecord.selectNodes(strEntityName);
      for(var i=objNodes.size()-1;i>=0;i--) {
        var objNode = objNodes.get(i);
        objNode.getParent().removeChild(objNode);
      }

      //loop through all controls to read the rolodex values from the UI and back into the cache (but only if not empty)
      for(var i=0;i<objTP.getChildren().length-1;i++) {
        var strValue = objTP.getChild(i).getDescendantsOfType(jsx3.gui.TextBox)[0].getValue();
        if(!jsx3.util.strEmpty(strValue)) {
          var objNode = objRecord.createNode(jsx3.xml.Entity.TYPEELEMENT,strEntityName);
          objRecord.appendChild(objNode);
          objNode.setAttribute("jsxid",strValue);
        }
      }
    }

    //favorite the posting or update the existing favorite (if already a favorite); NOTE: searchHistory is the 'hard' cache where the favorites are stored
    var bWasFavorited = gicx.Posting.favorite(objRecord.getAttribute("jsxid"));
    if(!bWasFavorited)
      gicx.Cache.setRecord("searchHistory", objRecord, gicx.Cache.getDocument("searchHistory").selectSingleNode("//favorites"));
  };


  /**
   * Called after the user has finished editing a given contact item (phone or email). Caches the change and reformats the UI to display the edit.
   * @param objDex {jsx3.gui.Block}
   */
  Contact.editDexItem = function(objDex) {
    objDex.getChild("lblContactObject").setDisplay("",true);
    objDex.getChild("txtContactObject").setDisplay("none",true);
    objDex.getChild("btnSave").setDisplay("none",true);
    objDex.getChild("btnEdit").setVisibility("",true);
    objDex.getChild("btnDelete").setVisibility("",true);
    gicx.Contact.cacheDexItem(objDex.getParent());
    var objChild = objDex.getDescendantsOfType(jsx3.gui.TextBox)[0];
    var SUBJECT = (objDex.getParent().getName() == "email") ? 'CONTACT_EMAIL_DID_CHANGE' : 'CONTACT_NUMBER_DID_CHANGE';
    gicx.publish({subject:SUBJECT,value:objChild.getValue(),jsxid:objChild.getId(),system:true});
  };


  /**
   * Called when user wants to edit a contact item (email or phone); basically displays a textbox to the user to enter the update
   * @param objDex {jsx3.gui.Block}
   */
  Contact.beginEditDexItem = function(objDex) {
    objDex.getChild("lblContactObject").setDisplay("none",true);
    objDex.getChild("txtContactObject").setDisplay("",true);
    objDex.getChild("btnSave").setDisplay("",true);
    objDex.getChild("btnEdit").setVisibility("hidden",true);
    objDex.getChild("btnDelete").setVisibility("hidden",true);
    jsx3.sleep(function() {
      objDex.getChild("txtContactObject").focus();
    });
  };


  /**
   * Removes the given email or phone link; calls method to save the updated posting
   * @param objDex {jsx3.gui.Block}
   */
  Contact.deleteDexItem = function(objDex) {
    var objParent = objDex.getParent();
    objParent.removeChild(objDex);
    gicx.Contact.cacheDexItem(objParent);
  };


  /**
   * Adds a new rolodex entry and returns (an instance of jsx3.gui.Block) -- the newly added rolodex item
   * @param objJSX {jsx3.gui.Block} parent to contain the new contact item (only three allowed)
   */
  Contact.addDexItem = function(objJSX) {
    //only 3 dex items allowed
    if(objJSX.getChildren().length < 4) {
      var c = objJSX.loadAndCache("components/contact_object.xml",false);
      objJSX.insertBefore(c,objJSX.getFirstChild());
      return c;
    } else {
      jsx3.log("Not allowed to have more than three dex items.");
    }
  };

});

//subscribe various module methods to application events
gicx.APP.subscribe("POSTING_WAS_ACTIVATED",gicx.Contact.onActivatePosting);
gicx.APP.subscribe("POSTING_WAS_DEACTIVATED",gicx.Contact.onDeactivatePosting);
gicx.APP.subscribe("CONTACT_NUMBER_DID_CHANGE",gicx.Contact.onUpdatePhone);
gicx.APP.subscribe("CONTACT_EMAIL_DID_CHANGE",gicx.Contact.onUpdateEmail);
gicx.APP.subscribe("POSTING_WAS_CLOSED",gicx.Contact.removeMarker);




